package com.fsh.lingsp.common.user.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fsh.lingsp.common.common.domain.vo.req.CursorPageBaseReq;
import com.fsh.lingsp.common.common.domain.vo.req.PageBaseReq;
import com.fsh.lingsp.common.common.domain.vo.resp.CursorPageBaseResp;
import com.fsh.lingsp.common.common.domain.vo.resp.PageBaseResp;
import com.fsh.lingsp.common.common.event.UserApplyEvent;
import com.fsh.lingsp.common.common.service.LockService;
import com.fsh.lingsp.common.common.utils.AssertUtil;
import com.fsh.lingsp.common.user.dao.UserApplyDao;
import com.fsh.lingsp.common.user.dao.UserDao;
import com.fsh.lingsp.common.user.dao.UserFriendDao;
import com.fsh.lingsp.common.chat.domain.entity.RoomFriend;
import com.fsh.lingsp.common.user.domain.entity.User;
import com.fsh.lingsp.common.user.domain.entity.UserApply;
import com.fsh.lingsp.common.user.domain.entity.UserFriend;
import com.fsh.lingsp.common.user.domain.enums.ApplyStatusEnum;
import com.fsh.lingsp.common.user.domain.vo.req.friend.FriendApplyReq;
import com.fsh.lingsp.common.user.domain.vo.req.friend.FriendApproveReq;
import com.fsh.lingsp.common.user.domain.vo.req.friend.FriendCheckReq;
import com.fsh.lingsp.common.user.domain.vo.resp.friend.FriendApplyResp;
import com.fsh.lingsp.common.user.domain.vo.resp.friend.FriendCheckResp;
import com.fsh.lingsp.common.user.domain.vo.resp.friend.FriendResp;
import com.fsh.lingsp.common.user.domain.vo.resp.friend.FriendUnreadResp;
import com.fsh.lingsp.common.user.service.FriendService;
import com.fsh.lingsp.common.chat.service.RoomService;
import com.fsh.lingsp.common.user.service.adapter.FriendAdapter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 作者：fsh
 * 日期：2024/03/08
 * <p>
 * 描述：好友服务实现
 */
@Service
@Slf4j
public class FriendServiceImpl implements FriendService {
    @Autowired
    private UserFriendDao userFriendDao;
    @Autowired
    private UserDao userDao;
    @Autowired
    private UserApplyDao userApplyDao;
    @Autowired
    private RoomService roomService;
    @Autowired
    private LockService lockService;
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    /**
     * 获取当前用户的好友列表
     *
     * @param uid 当前用户uid
     * @param req 游标翻页
     */
    @Override
    public CursorPageBaseResp<FriendResp> friendList(Long uid, CursorPageBaseReq req) {
        //游标翻页查询
        CursorPageBaseResp<UserFriend> friendPage=userFriendDao.getFriendPage(uid,req);
        //没有查到，空列表，没有好友
        if (CollectionUtil.isEmpty(friendPage.getList())){
            return CursorPageBaseResp.empty();
        }
        //取出所有好友的uid
        List<Long> friendUids = friendPage.getList().stream()
                .map(UserFriend::getFriendUid)
                .collect(Collectors.toList());
        //通过好友uid查询他们的在线信息
        List<User> userList=userDao.getFriendList(friendUids);
        //构造返回
        return CursorPageBaseResp.init(friendPage, FriendAdapter.buildFriend(friendPage.getList(),userList));
    }

    /**
     * 批量判断是否是自己好友
     *
     * @param uid
     * @param request  其他用户uid列表
     */
    @Override
    public FriendCheckResp check(Long uid, FriendCheckReq request) {
        //确定好友列表
        List<UserFriend> userFriends=userFriendDao.getByFriends(uid,request.getUidList());
        //取出所有的friendId
        List<Long> list = userFriends.stream().map(UserFriend::getFriendUid).collect(Collectors.toList());

        //对比userFriends 和 request.getUidList()。 判断是否是好友，构建实体，封装返回。
        List<FriendCheckResp.FriendCheck> friendCheckList = request.getUidList().stream().map(friendUid -> {
            FriendCheckResp.FriendCheck friendCheck = new FriendCheckResp.FriendCheck();
            friendCheck.setUid(friendUid);
            friendCheck.setIsFriend(list.contains(friendUid));
            return friendCheck;
        }).collect(Collectors.toList());

        return new FriendCheckResp(friendCheckList);
    }

    /**
     * 普通翻页 获取好友申请列表
     *
     * @param uid  当前用户的uid
     * @param request  翻页信息
     */
    @Override
    public PageBaseResp<FriendApplyResp> pageApplyFriend(Long uid, PageBaseReq request) {
        IPage<UserApply> userApplyIPage=userApplyDao.friendApplyPage(uid,request.plusPage());
        if (CollectionUtil.isEmpty(userApplyIPage.getRecords())){
            return PageBaseResp.empty();
        }
        //将申请列表设为已读↓
        readAlready(uid,userApplyIPage);
        //返回消息
        return PageBaseResp.init(userApplyIPage,FriendAdapter.buildFriendApplyList(userApplyIPage.getRecords()));
    }

    //将申请列表设为已读
    private void readAlready(Long uid, IPage<UserApply> userApplyIPage) {
        List<Long> idLIst = userApplyIPage.getRecords()
                .stream().map(UserApply::getId)
                .collect(Collectors.toList());
        userApplyDao.readAlready(uid,idLIst);
    }

    /**
     * 返回申请列表的未读数
     *
     * @param uid 当前用户的uid
     */
    @Override
    public FriendUnreadResp unread(Long uid) {
        Integer unReadCount=userApplyDao.getUnReadCount(uid);
        return new FriendUnreadResp(unReadCount);
    }

    /**
     * 同意好友请求。使用了分布式锁(todo 改成注解)
     *
     * @param uid  当前用户uid
     * @param request   里面包含申请好友这条记录的主键id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void applyApprove(Long uid, FriendApproveReq request) {
        //上分布式锁
        lockService.executeWithLock(uid.toString(),()->{
            //首先查询数据库，看看到底有没有这条申请记录。
            UserApply userApply = userApplyDao.getById(request.getApplyId());
            AssertUtil.isNotEmpty(userApply,"不存在申请记录");
            AssertUtil.equal(userApply.getTargetId(),uid,"不存在申请记录");
            AssertUtil.equal(userApply.getStatus(), ApplyStatusEnum.WAIT_APPROVAL.getStatus(),"已同意好友申请");

            //接下来就是同意申请
            userApplyDao.agree(request.getApplyId());
            //创建双方好友关系
            createFriend(uid,userApply.getUid());
            //创建一个聊天房间
            RoomFriend roomFriend=roomService.createFriendRoom(Arrays.asList(uid,userApply.getUid()));

            // todo 发送一条同意消息。我们已经是好友了，开始聊天吧!
        });
    }

    // 创建双方好友关系，插入两条记录。当前用户的uid，申请人的uid1
    private void createFriend(Long uid, Long uid1) {
        //他是我的好友
        UserFriend uf1=new UserFriend();
        uf1.setUid(uid);
        uf1.setFriendUid(uid1);
        //我也是他的好友
        UserFriend uf2=new UserFriend();
        uf2.setUid(uid1);
        uf2.setFriendUid(uid);
        //插入数据库
        userFriendDao.saveBatch(CollectionUtil.toList(uf1,uf2));
    }

    /**
     * 删除好友
     *
     * @param uid  当前用户uid
     * @param friendUid   要删除的好友uid
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteFriend(Long uid, Long friendUid) {
        List<UserFriend> userFriends=userFriendDao.getUserFriend(uid,friendUid);
        if (CollectionUtil.isEmpty(userFriends)){
            log.info("没有好友关系：{},{}", uid, friendUid);
            return;
        }
        //取出id
        List<Long> idList = userFriends.stream().map(UserFriend::getId).collect(Collectors.toList());
        //批量删除
        userFriendDao.removeByIds(idList);
        //禁用房间
        roomService.disableFriendRoom(Arrays.asList(uid,friendUid));
    }

    /**
     * 申请好友。 使用了分布式锁（(todo 改成注解。这里记得使用注解式分布式锁)）
     *
     * @param uid  当前用户uid
     * @param req   被申请人的req (包括申请信息和他的uid)
     */
    @Override
    public void apply(Long uid, FriendApplyReq req) {
        lockService.executeWithLock(uid.toString(),()->{
            //首先确认两者有没有好友关系
            UserFriend userFriend=userFriendDao.getByFriend(uid,req.getTargetUid());
            AssertUtil.isEmpty(userFriend,"你们已经是好友了");

            //是否有待审批的申请记录（自己的），以前已经申请过了，就不能再申请了
            UserApply myApply=userApplyDao.getFriendApproving(uid,req.getTargetUid());
            if (Objects.nonNull(myApply)){
                log.info("已有好友申请记录,uid:{}, targetId:{}", uid, req.getTargetUid());
                return;
            }

            //是否有待审批的申请记录(别人请求自己的)
            UserApply hisApproving = userApplyDao.getFriendApproving(req.getTargetUid(), uid);
            if (Objects.nonNull(hisApproving)){
                //有的话，直接同意
                applyApprove(uid, new FriendApproveReq(hisApproving.getId()));
//            ((FriendService) AopContext.currentProxy()).applyApprove(uid, new FriendApproveReq(hisApproving.getId()));
                return;
            }

            //申请记录 入库
            UserApply insert=FriendAdapter.buildFriendApply(uid,req);
            userApplyDao.save(insert);

            // todo 申请事件
            applicationEventPublisher.publishEvent(new UserApplyEvent(this,insert));
        });
    }
}
